<?php
namespace app\admin\library\traits;

use bang\validate\Validate;
use bang\validate\ValidateException;
use support\Controller;
use support\Db;
use support\exception\Exception;
use support\Request;
use app\common\extra\Status;
use think\db\exception\PDOException;

/**
 * 后台公共方法
 * Class BackApi
 * @package app\admin\library\traits
 */
class BackApi extends Controller
{

    /**
     * 主键参数key
     * @var string
     */
    protected $pk = 'ids';

    /**
     * 快速搜索时执行查找的字段
     */
    protected $searchFields = 'id';

    /**
     * 是否是关联查询
     */
    protected $relationSearch = false;

    /**
     * 模型验证的命名空间
     * @var null
     */
    protected $validate = null;

    /**
     * 实例模型
     * @var \support\Model
     */
    protected $model = null;

    /**
     * 添加时，能添加的字段
     * @var bool
     */
    protected $addAllowField = true;

    /**
     * 编辑时，能修改的字段
     * @var bool
     */
    protected $editAllowField = true;

    /**
     * 禁止访问的方法
     * @var array
     */
    protected $forbidAction = [];

    /**
     * BackApi constructor.
     */
    public function __construct()
    {
        parent::__construct();
    }


    /**
     * 简单的验证方法
     * @param $className
     * @param array $data
     * @param string|null $scene
     * @return bool
     * @throws ValidateException
     */
    public function validate($className , $data = [] ,  string $scene = null)
    {
        if(class_exists($className)){
            /**
             * @param \bang\validate\Validate $validateObject
             */
            $validateObject = new $className;
            if(!empty($scene)){
                if(!$validateObject->scene($scene)->check($data)){
                    throw new ValidateException($validateObject->getError());
                }
            }else{
                if(!$validateObject->check($data)){
                    throw new ValidateException($validateObject->getError());
                }
            }
        }else{
            throw new ValidateException("Class " . $className . " not found");
        }
        return true;
    }

    /**
     * 列表查看
     * @param Request $request
     * @return \support\Response
     */
    public function index(Request $request)
    {
        if(!$this->checkForbidAction($request)){
            return $this->error("禁止访问" , null , Status::CODE_FORBIDDEN);
        }

        if($request->isGet()){
            list($where , $sort , $order , $page , $pageSize) = $this->buildparams($request);
            $model = new $this->model;
            $total = $model
                ->where($where)
                ->order($sort, $order)
                ->count();

            $list = $model
                ->where($where)
                ->order($sort, $order)
                ->page($page, $pageSize)
                ->select();

            $list = collection($list)->toArray();
            $result = array("total" => $total, "rows" => $list);

            return $this->success("获取成功" , $result);
        }else{
            return $this->error("请求方式出错" , null , Status::CODE_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * 添加api的公共方法
     * @param Request $request
     * @return \support\Response
     */
    public function add(Request $request)
    {
        if(!$this->checkForbidAction($request)){
            return $this->error("禁止访问" , null , Status::CODE_FORBIDDEN);
        }
        if(!$request->isPost()){
            return $this->error("请求方式出错" , null , Status::CODE_METHOD_NOT_ALLOWED);
        }
        $params = $request->post();
        //不为空时，才去验证数据
        if(!empty($this->validate) && class_exists($this->validate)){
            $validate = new $this->validate;
            $validateRes = $validate->scene('add')->check($params);
            if(!$validateRes){
                return $this->error($validate->getError() , null , Status::CODE_BAD_REQUEST);
            }
        }
        //开启事务
        Db::startTrans();
        try {
            $model = new $this->model;
            $result = $model->allowField($this->addAllowField)->save($params);
            if($result === false){
                throw new Exception("操作失败");
            }
        }catch(PDOException $exception){
            Db::rollback();
            return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
        }catch (Exception $exception){
            Db::rollback();
            return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
        }
        Db::commit();
        return $this->success();
    }

    /**
     * 修改api的公共方法
     * @param Request $request
     * @param null $ids
     * @return \support\Response
     */
    public function edit(Request $request)
    {
        if(!$this->checkForbidAction($request)){
            return $this->error("禁止访问" , null , Status::CODE_FORBIDDEN);
        }
        if(!$request->isPut()){
            return $this->error("请求方式出错" , [] , Status::CODE_METHOD_NOT_ALLOWED);
        }

        $params = $request->post();
        $ids = $params[$this->pk] ?? null;
        $model = new $this->model;
        $row = $model->find($ids);
        if(empty($row)){
            return $this->error("记录不存在" , null , Status::CODE_BAD_REQUEST);
        }
        //不为空时，才去验证数据
        if(!empty($this->validate) && class_exists($this->validate)){
            $validate = new $this->validate;
            $validateRes = $validate->scene('edit')->check($params);
            if(!$validateRes){
                return $this->error($validate->getError() , null , Status::CODE_BAD_REQUEST);
            }
        }

        //开启事务
        Db::startTrans();
        try {
            $result = $row->allowField($this->editAllowField)->save($params);
            if($result === false){
                throw new Exception("操作失败");
            }
        }catch(PDOException $exception){
            Db::rollback();
            return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
        }catch (Exception $exception){
            Db::rollback();
            return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
        }
        Db::commit();
        return $this->success();
    }

    /**
     * 删除api的公共方法
     * @param Request $request
     * @param string $ids
     * @return \support\Response
     */
    public function del(Request $request)
    {
        if(!$this->checkForbidAction($request)){
            return $this->error("禁止访问" , null , Status::CODE_FORBIDDEN);
        }
        if(!$request->isDelete()){
            return $this->error("请求方式出错" , [] , Status::CODE_METHOD_NOT_ALLOWED);
        }
        $ids = $request->get($this->pk , null);
        if(!empty($ids)){
            $model = new $this->model;
            $pk = $model->getPk();
            $list = $model->where($pk , 'IN' , $ids)->select();
            if(!empty($list)){
                Db::startTrans();
                try {
                    $count = 0;
                    foreach ($list as $item) {
                        if($item->delete() === true){
                            $count++;
                        }
                    }
                    if(empty($count)){
                        throw new Exception("操作失败");
                    }
                }catch(PDOException $exception){
                    Db::rollback();
                    return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
                }catch (Exception $exception){
                    Db::rollback();
                    return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
                }
            }
        }else{
            return $this->error("缺少" . $this->pk ."参数");
        }

        Db::commit();
        return $this->success();
    }

    /**
     * 真实删除api的公共方法
     * @param Request $request
     * @param string $ids
     * @return \support\Response
     */
    public function destroy(Request $request)
    {
        if(!$this->checkForbidAction($request)){
            return $this->error("禁止访问" , null , Status::CODE_FORBIDDEN);
        }
        if(!$request->isDelete()){
            return $this->error("请求方式出错" , [] , Status::CODE_METHOD_NOT_ALLOWED);
        }
        $ids = $request->get($this->pk , null);

        if(!empty($ids)){
            $model = new $this->model;
            $pk = $model->getPk();
            $list = $model->where($pk , 'IN' , $ids)->onlyTrashed()->select();
            if(!empty($list)){
                Db::startTrans();
                try {
                    $count = 0;
                    foreach ($list as $item) {
                        if($item->force()->delete() === true){
                            $count++;
                        }
                    }
                    if(empty($count)){
                        throw new Exception("操作失败");
                    }
                }catch(PDOException $exception){
                    Db::rollback();
                    return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
                }catch (Exception $exception){
                    Db::rollback();
                    return $this->error($exception->getMessage() , null , Status::CODE_INTERNAL_SERVER_ERROR );
                }
            }
        }else{
            return $this->error("缺少" . $this->pk ."参数");
        }
        Db::commit();
        return $this->success();
    }

    /**
     * 详情
     * @param Request $request
     * @param int $ids
     * @return \support\Response
     */
    public function detail(Request $request){
        if(!$this->checkForbidAction($request)){
            return $this->error("禁止访问" , null , Status::CODE_FORBIDDEN);
        }
        if(!$request->isGet()){
            return $this->error("请求方式出错" , [] , Status::CODE_METHOD_NOT_ALLOWED);
        }
        $ids = $request->get($this->pk , null);
        if(!empty($ids)){
            $model = new $this->model;
            $row = $model->find($ids);
            return $this->success("获取成功" , $row);
        }else{
            return $this->error("缺少" . $this->pk ."参数");
        }
    }

    /**
     * 检查是否方法是否可以访问
     * @param Request $request
     * @return bool
     */
    protected function checkForbidAction(Request $request)
    {
        if(!empty($this->forbidAction) && in_array( $request->action , $this->forbidAction)){
            return false;
        }
        return true;
    }

    /**
     * 列表中处理参数
     * @param Request $request
     * @param null $searchFields
     * @param null $relationSearch
     * @return array
     */
    protected function buildParams(Request $request , $searchFields = null, $relationSearch = null){
        $searchFields = is_null($searchFields) ? $this->searchFields : $searchFields;
        $relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
        $search = $request->get('search' , '');
        $filter = $request->get('filter' , '');
        $op = $request->get("op", '');
        $sort = $request->get("sort", "id");
        $order = $request->get("order", "DESC");
        $page = $request->get("page", 0);
        $limit = $request->get("limit", 0);
        $filter = json_decode($filter, TRUE);
        $op = json_decode($op, TRUE);
        $filter = $filter ? $filter : [];
        $where = [];
        $tableName = '';

        if($relationSearch){
            if (!empty($this->model)){
                $model = new $this->model;
                $tableName = $model->getTable() . ".";
            }
            $sort = stripos($sort, ".") === false ? $tableName . $sort : $sort;
        }
        if($search){
            $searChar = is_array($searchFields) ? $searchFields : explode(',', $searchFields);
            foreach ($searChar as $k => &$v)
            {
                $v = stripos($v, ".") === false ? $tableName . $v : $v;
            }
            unset($v);
            $where[] = [ implode("|", $searChar), "LIKE", "%{$search}%" ];
        }

        foreach ($filter as $k => $v)
        {
            $sym = isset($op[$k]) ? $op[$k] : '=';
            if (stripos($k, ".") === false)
            {
                $k = $tableName . $k;
            }
            $v = !is_array($v) ? trim($v) : $v;
            $sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
            switch ($sym)
            {
                case '=':
                case '!=':
                    $where[] = [$k, $sym, (string) $v];
                    break;
                case 'LIKE':
                case 'NOT LIKE':
                case 'LIKE %...%':
                case 'NOT LIKE %...%':
                    $where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{$v}%"];
                    break;
                case '>':
                case '>=':
                case '<':
                case '<=':
                    $where[] = [$k, $sym, intval($v)];
                    break;
                case 'FINDIN':
                case 'FIND_IN_SET':
                    $where[] = "FIND_IN_SET('{$v}', `{$k}`)";
                    break;
                case 'IN':
                case 'IN(...)':
                case 'NOT IN':
                case 'NOT IN(...)':
                    $where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)];
                    break;
                case 'BETWEEN':
                case 'NOT BETWEEN':
                    $arr = array_slice(explode(',', $v), 0, 2);
                    if (stripos($v, ',') === false || !array_filter($arr)){
                        break;
                    }
                    //当出现一边为空时改变操作符
                    if ($arr[0] === ''){
                        $sym = $sym == 'BETWEEN' ? '<=' : '>';
                        $arr = $arr[1];
                    }elseif ($arr[1] === ''){
                        $sym = $sym == 'BETWEEN' ? '>=' : '<';
                        $arr = $arr[0];
                    }
                    $where[] = [$k, $sym, $arr];
                    break;
                case 'RANGE':
                case 'NOT RANGE':
                    $v = str_replace(' - ', ',', $v);
                    $arr = array_slice(explode(',', $v), 0, 2);
                    if (stripos($v, ',') === false || !array_filter($arr)){
                        break;
                    }
                    //当出现一边为空时改变操作符
                    if ($arr[0] === '')
                    {
                        $sym = $sym == 'RANGE' ? '<=' : '>';
                        $arr = $arr[1];
                    }
                    else if ($arr[1] === '')
                    {
                        $sym = $sym == 'RANGE' ? '>=' : '<';
                        $arr = $arr[0];
                    }
                    $where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' time', $arr];
                    break;
                case 'LIKE':
                case 'LIKE %...%':
                    $where[] = [$k, 'LIKE', "%{$v}%"];
                    break;
                case 'NULL':
                case 'IS NULL':
                case 'NOT NULL':
                case 'IS NOT NULL':
                    $where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
                    break;
                default:
                    break;
            }
        }
        $where = function($query) use ($where) {
            foreach ($where as $k => $v)
            {
                if (is_array($v)){
                    call_user_func_array([$query, 'where'], $v);
                }
                else
                {
                    $query->where($v);
                }
            }
        };
        return [$where, $sort, $order, $page, $limit];
    }



}